/**
 * Class: MeshTest
 *
 * Purpose: 
 *
 * Created: Feb 19, 2010 @ 11:50:50 AM
 * @author Ken Scott
 */
package org.openfantasia.ps.particle;

import org.openfantasia.util.math.DPoint3;
import org.openfantasia.util.math.PMath;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class Polygon extends Particle {

	protected List<DPoint3> vertices = new ArrayList<DPoint3>();

	protected Random random = new Random(0);


	public Polygon() {
	}


	public Polygon(long randSeed) {
		random = new Random(randSeed);
	}


	public Polygon(DPoint3... pa) {
		setVertices(pa);
	}


	public Polygon(Polygon that) {
		copy(that);
	}


	public void copy(Polygon that) {
		super.copy(that);

		this.vertices.clear();
		this.vertices.addAll(Arrays.asList(DPoint3.copy(vertices.toArray(new DPoint3[vertices.size()]))));
	}


	public Polygon copy() {
		return new Polygon(this);
	}


	public void setVertices(DPoint3... pa) {
		vertices.clear();
		vertices.addAll(Arrays.asList(pa));
	}


	public DPoint3[] getVertices() {
		return DPoint3.copy(vertices.toArray(new DPoint3[vertices.size()]));
	}


	public void add(DPoint3 p) {
		if (!vertices.contains(p)) {
			vertices.add(p);
		}
	}


	public void remove(DPoint3 p) {
		vertices.remove(p);
	}


	public void setRandSeed(long seed) {
		random = new Random(seed);
	}


	/**
	 * Subdivide this patch into six sub-triangles, adding 'noise' to each of the new vertices.
	 *
	 * @param noise
	 * @return
	 */
	public List<Polygon> subdivide(DPoint3 noise, double minNoise, double maxNoise) {
		List<Polygon> result = new ArrayList<Polygon>();

		double dNode = 1.0d / (double) vertices.size();

		// Find the 'initialPosition'
		DPoint3 center = new DPoint3();
		for (DPoint3 node : vertices) {
			center.add(node);
		}
		center.scale(dNode);

		// Find the average delta from the initialPosition
		double delta = 0;
		for (DPoint3 node : vertices) {
			delta += center.copy().sub(node).mag();
		}
		delta *= dNode;

		// Noise-ify the initialPosition
		center.add(noise.copy().scale(delta * PMath.random(minNoise, maxNoise)));

		// Find the midpoints on the edges
		List<DPoint3> mids = new ArrayList<DPoint3>();
		for (int i = 0; i < vertices.size(); i++) {
			DPoint3 i0;
			DPoint3 i1;
			if (i == 0) {
				i0 = vertices.get(0);
				i1 = vertices.get(vertices.size() - 1);
			}
			else {
				i0 = vertices.get(i);
				i1 = vertices.get(i - 1);
			}
			DPoint3 m = i0.copy().add(i1);
			m.scale(0.5d);
			double mag = m.copy().sub(i0).mag();

			// Add noise
			m.add(noise.copy().scale(mag * PMath.random(minNoise, maxNoise)));

			mids.add(m);
		}

		// Return a new set of polygons
		for (int i = 0; i < vertices.size(); i++) {
			Polygon polygon;

			polygon = new Polygon();
			polygon.add(vertices.get(i));
			polygon.add(center);
			polygon.add(mids.get(i));
			result.add(polygon);

			polygon = new Polygon();
			if (i == 0) {
				polygon.add(mids.get(0));
				polygon.add(center);
				polygon.add(vertices.get(vertices.size() - 1));
			}
			else {
				polygon.add(mids.get(i));
				polygon.add(center);
				polygon.add(vertices.get(i - 1));
			}
			result.add(polygon);
		}

		return result;
	}


	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof Polygon)) {
			return false;
		}

		Polygon polygon = (Polygon) o;

		if (vertices != null ? !vertices.equals(polygon.vertices) : polygon.vertices != null) {
			return false;
		}

		return true;
	}


	@Override
	public int hashCode() {
		return vertices != null ? vertices.hashCode() : 0;
	}
}
